﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Cofe.Core;
using Cofe.Core.EntryLinks;
using Cofe.Core.Filter;
using Cofe.Core.Interfaces;
using Cofe.Core.Property;
using Cofe.Core.Service;

namespace Cofe.Data.Implements
{
    /// <summary>
    /// Store EntryLink data in a database.
    /// </summary>
    public class DBEntryLinkContainerBehavior : IContainerBehavior
    {
        #region Constructor


        [Export(typeof(ICofeService))]
        [ServicePriority(ServicePriorityAttribute.DefaultPriority_COFEDB)]
        public class SecondaryPPFactory : ISecondaryPropertyProviderFactory
        {
            public IEnumerable<IPropertyProvider> GetSecoondaryPropertyProviders(IPropertyHost propertyHost)
            {
                if (!(propertyHost.AllPropertyProviders.Any(pp => pp is IEntryLinkSecondaryPP)))
                {
                    Guid entryId = propertyHost.Behaviors.GetProperty<Guid>(CacheProperties.Id);
                    string parseName = propertyHost.Behaviors.GetProperty<string>(CofeProperties.FullParseName);
                    yield return new EntryLinkSecondaryPP(new DBEntryLinkContainerBehavior(entryId, parseName));
                }
            }
            public IServiceLocater ServiceLocater { get; set; }
        }

        [Export(typeof(ICofeService))]
        [ServicePriority(ServicePriorityAttribute.DefaultPriority_COFEDB)]
        public class PrimaryPPRegenerator : IEntryPrimaryPPRegenerator
        {
            public Task<IPropertyProvider> RegenerateAsync(IPropertyCache propertyCache)
            {
                if (propertyCache.IsPropertySupported(CofeEntryLinkProperties.LinkedEntryPH))
                {
                    var parentParseName = propertyCache.GetOneProperty<string>(CofeProperties.ParentParseName);
                    var parentId = propertyCache.GetOneProperty<Guid>(CacheProperties.ParentId);
                    var label = propertyCache.GetOneProperty<string>(CofeProperties.Label);
                    var linkedEntry = propertyCache.GetOneProperty<IPropertyHost>(CofeEntryLinkProperties.LinkedEntryPH);
                    if (linkedEntry != null)
                    {
                        string linkedParseName = linkedEntry.Behaviors.GetProperty<string>(CofeProperties.FullParseName);
                        var cb = new DBEntryLinkContainerBehavior(parentId, parentParseName);
                        return Task.FromResult<IPropertyProvider>(new EntryLinkPrimaryPP(cb, parentParseName, label, linkedParseName));
                    }
                }
                return Task.FromResult<IPropertyProvider>(null);
            }
        }

        internal DBEntryLinkContainerBehavior(Guid parentId, string parentParseName)
        {
            this.ParentEntryId = parentId;
            this.ParentParseName = parentParseName;
        }

        #endregion

        #region Methods

        public IPropertyHost Add(int position, string label, string targetPath, ParsableType parsableType)
        {
            using (var r = CofeDBServices.RepositoryFactory.CreateRepository())
            {
                if (position == -1)
                    position = r.Count(FilterCollections.FromParentGuid(ParentEntryId));

                if (parsableType != ParsableType.ParsePath && parsableType != ParsableType.ParsePathLookup)
                {
                    var targetEntry = CofeServices.PathParserManager.Behaviors.TryParsePath(targetPath, parsableType);
                    if (targetEntry == null)
                        throw new System.IO.FileNotFoundException(targetPath);
                    targetPath = targetEntry.Behaviors.GetProperty<string>(CofeProperties.FullParseName);
                }

                string finalLabel = label;
                //int ctr = 1;
                //while (r.Count(FilterCollections.FromParseName(PathFE.Combine(ParentParseName, finalLabel))) > 0)
                //    finalLabel = label + ctr++.ToString();

                var newPP = new EntryLinkPrimaryPP(this, ParentParseName, finalLabel, targetPath);
                newPP.OverridePosition = position;

                var newPH = CofeServices.EntryConstructor.ConstructPropertyHost(newPP);
                r.AddOrUpdate(true, false, newPH);
                r.SaveChanges();

                newPP.OverridePosition = null; //Load from database.

                return newPH;
            }
        }

        public IEnumerable<string> GetLabels()
        {
            List<string> retVal = new List<string>();
            using (var r = CofeDBServices.RepositoryFactory.CreateRepository())
            {
                foreach (var de in r.FindDbEntry(FilterCollections.FromParentGuid(ParentEntryId).AndEntryType(EntryTypes.Link)))
                    retVal.Add(PathFE.GetFileName(de.ParseName));
            }
            return retVal;
        }

        public IPropertyHost GetItem(string label)
        {
            using (var r = CofeDBServices.RepositoryFactory.CreateRepository())
            {
                var foundEntry = r.FindAll(FilterCollections.FromParentGuid(ParentEntryId).AndName(label)).FirstOrDefault();
                if (foundEntry == null)
                    throw new System.IO.FileNotFoundException();
                return foundEntry;
            }
        }

        public void Remove(string label)
        {
            using (var r = CofeDBServices.RepositoryFactory.CreateRepository())
            {
                r.Remove(FilterCollections.FromParentGuid(ParentEntryId).AndName(label));
                r.SaveChanges();
            }
        }

        public int GetPosition(string label)
        {
            using (var r = CofeDBServices.RepositoryFactory.CreateRepository())
            {
                var foundEntry = r.FindDbEntry(FilterCollections.FromParentGuid(ParentEntryId).AndName(label)).FirstOrDefault();
                if (foundEntry != null)
                    return foundEntry.Position;
                return -1;
            }
        }

        public void SetPosition(string label, int position)
        {
            using (var r = CofeDBServices.RepositoryFactory.CreateRepository())
            {
                var foundEntry = r.FindDbEntry(FilterCollections.FromParentGuid(ParentEntryId).AndName(label)).FirstOrDefault();
                if (foundEntry != null)
                {
                    r.ChangeValue(foundEntry, de => de.Position, position);
                    r.SaveChanges();
                }
            }
        }

        public void Rename(string label, string newLabel)
        {
            using (var r = CofeDBServices.RepositoryFactory.CreateRepository())
            {
                var targetEntry = r.FindDbEntry(FilterCollections.FromParentGuid(ParentEntryId).AndName(newLabel)).FirstOrDefault();
                if (targetEntry != null)
                    throw new ArgumentException("Target already exists.");

                var foundEntry = r.FindDbEntry(FilterCollections.FromParentGuid(ParentEntryId).AndName(label)).FirstOrDefault();

                if (foundEntry != null)
                {
                    r.ChangeValue(foundEntry, de => de.Label, newLabel);
                    r.ChangeValue(foundEntry, de => de.ParseName, PathFE.Combine(ParentParseName, newLabel));
                    r.SaveChanges();
                }
            }
        }

        #endregion

        #region Data

        #endregion

        #region Public Properties

        public Guid ParentEntryId { get; private set; }
        public string ParentParseName { get; private set; }

        #endregion



    }
}
